home *** CD-ROM | disk | FTP | other *** search
/ Aminet 40 / Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso / Aminet / dev / src / xad_ar.lha / ar.c < prev    next >
C/C++ Source or Header  |  2000-08-06  |  11KB  |  319 lines

  1. /* UNIX ar(1) file archiver client for XAD.
  2.  * Copyright (C) 2000 Stuart Caie <kyzer@4u.net>
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  * 
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  * 
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  */
  18.  
  19. /* this is very much based on the GNU binutils/bfd/archive.c,
  20.  * written by Cygnus Support */
  21.  
  22. /* Assumes:
  23.    o - all archive elements start on an even boundary, newline padded;
  24.    o - all arch headers are char *;
  25.    o - all arch headers are the same size (across architectures).
  26. */
  27.  
  28. /* Some formats provide a way to cram a long filename into the short
  29.    (16 chars) space provided by a BSD archive.  The trick is: make a
  30.    special "file" in the front of the archive, sort of like the SYMDEF
  31.    entry.  If the filename is too long to fit, put it in the extended
  32.    name table, and use its index as the filename.  To prevent
  33.    confusion prepend the index with a space.  This means you can't
  34.    have filenames that start with a space, but then again, many Unix
  35.    utilities can't handle that anyway.
  36.  
  37.    This scheme unfortunately requires that you stand on your head in
  38.    order to write an archive since you need to put a magic file at the
  39.    front, and need to touch every entry to do so.  C'est la vie.
  40.  
  41.    We support two variants of this idea:
  42.    The SVR4 format (extended name table is named "//"),
  43.    and an extended pseudo-BSD variant (extended name table is named
  44.    "ARFILENAMES/").  The origin of the latter format is uncertain.
  45.  
  46.    BSD 4.4 uses a third scheme:  It writes a long filename
  47.    directly after the header.  This allows 'ar q' to work.
  48.    We currently can read BSD 4.4 archives, but not write them.
  49. */
  50.  
  51. /* Summary of archive member names:
  52.  
  53.  Symbol table (must be first):
  54.  "__.SYMDEF       " - Symbol table, Berkeley style, produced by ranlib.
  55.  "/               " - Symbol table, system 5 style.
  56.  
  57.  Long name table (must be before regular file members):
  58.  "//              " - Long name table, System 5 R4 style.
  59.  "ARFILENAMES/    " - Long name table, non-standard extended BSD (not BSD 4.4).
  60.  
  61.  Regular file members with short names:
  62.  "filename.o/     " - Regular file, System 5 style (embedded spaces ok).
  63.  "filename.o      " - Regular file, Berkeley style (no embedded spaces).
  64.  
  65.  Regular files with long names (or embedded spaces, for BSD variants):
  66.  "/18             " - SVR4 style, name at offset 18 in name table.
  67.  "#1/23           " - Long name (or embedded paces) 23 characters long,
  68.               BSD 4.4 style, full name follows header.
  69.               Implemented for reading, not writing.
  70.  " 18             " - Long name 18 characters long, extended pseudo-BSD.
  71.  */
  72.  
  73.  
  74. #include "SDI_compiler.h"
  75. #include <exec/types.h>
  76. #include <exec/memory.h>
  77. #include <string.h>
  78. #include <libraries/xadmaster.h>
  79. #include <proto/xadmaster.h>
  80.  
  81.  
  82. #ifndef XADMASTERFILE
  83. #define ar_Client        FirstClient
  84. #define NEXTCLIENT        0
  85. const UBYTE version[] = "$VER: ar 1.1 (05.08.2000)";
  86. #endif
  87. #define AR_VERSION        1
  88. #define AR_REVISION        1
  89.  
  90. #define XADBASE  REG(a6, struct xadMasterBase *xadMasterBase)
  91.  
  92. #define ARMAG  "!<arch>\012"    /* For COFF and a.out archives */
  93. #define ARMAGB "!<bout>\012"    /* For b.out archives */
  94. #define ARFMAG "`\012"
  95.  
  96. struct ar_hdr {
  97.   char ar_name[16];        /* name of this member */
  98.   char ar_date[12];        /* file mtime */
  99.   char ar_uid[6];        /* owner uid; printed as decimal */
  100.   char ar_gid[6];        /* owner gid; printed as decimal */
  101.   char ar_mode[8];        /* file mode, printed as octal   */
  102.   char ar_size[10];        /* file size, printed as decimal */
  103.   char ar_fmag[2];        /* should contain ARFMAG */
  104. };
  105.  
  106. #define AR_HDRSIZE (sizeof(struct ar_hdr))
  107.  
  108. char *ar_memchr(char *src, int c, int length) {
  109.   while (length--) if (*src == c) return src; else src++;
  110.   return NULL;
  111. }
  112.  
  113. ULONG ar_readnum(char *str, int strl, int base) {
  114.   ULONG result=0;
  115.   int nums=0;
  116.   
  117.   while (strl--) {
  118.     char c = *str++;
  119.     if (nums) {
  120.       if (c < '0' || c > '9') break;
  121.       result *= base;
  122.       result += c - '0';
  123.     }
  124.     else if (c >= '0' && c <= '9') nums=1, result = c - '0';
  125.   }
  126.   return result;
  127. }
  128.  
  129.  
  130. ASM(BOOL) ar_RecogData(REG(d0, ULONG size), REG(a0, STRPTR d), XADBASE) {
  131.   return (BOOL) (strncmp(d, ARMAG, 8) == 0 || strncmp(d, ARMAGB, 8) == 0);
  132. }
  133.  
  134. ASM(LONG) ar_GetInfo(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
  135.   UBYTE *ext_names = NULL, *bsd_name = NULL, *namep, *pend;
  136.   ULONG filenum = 1, skiplen = 8, namelen, extnameslen;
  137.   struct xadFileInfo *link = NULL,  *fi;
  138.   LONG err = XADERR_OK;
  139.   struct ar_hdr hdr;
  140.  
  141.   struct TagItem filetags[]  = {
  142.     { XAD_OBJNAMESIZE, 0 },
  143.     { TAG_DONE, 0 }
  144.   };
  145.  
  146.   struct TagItem datetags[] = {
  147.     { XAD_DATEUNIX, 0 },
  148.     { XAD_GETDATEXADDATE, 0 },
  149.     { TAG_DONE, 0 }
  150.   };
  151.  
  152.   struct TagItem prottags[] = {
  153.     { XAD_PROTUNIX, 0 },
  154.     { XAD_GETPROTAMIGA, 0 },
  155.     { TAG_DONE, 0 }
  156.   };
  157.  
  158.   while (1) {
  159.     /* normal exit-point - will we skip out of the file? */
  160.     if ((ai->xai_InPos+skiplen) & 1) skiplen++; /* alignment */
  161.     if ((ai->xai_InPos+skiplen+AR_HDRSIZE) >= ai->xai_InSize) break;
  162.  
  163.     /* skip to next header, read it in (note initial skip of 8 bytes) */
  164.     if ((err = xadHookAccess(XADAC_INPUTSEEK, skiplen, NULL, ai))) break;
  165.     if ((err = xadHookAccess(XADAC_READ, AR_HDRSIZE, (APTR)&hdr, ai))) break;
  166.  
  167.     /* check magic header number */
  168.     if (hdr.ar_fmag[0] != 0x60 || hdr.ar_fmag[1] != 0x0A) {
  169.       err = XADERR_DATAFORMAT; break;
  170.     }
  171.  
  172.     /* ignore the symbol offsets magic file */
  173.     if (filenum == 1
  174.     && (strncmp(hdr.ar_name, "__.SYMDEF       ", 16) == 0
  175.     ||  strncmp(hdr.ar_name, "/               ", 16) == 0)) {
  176.       skiplen = ar_readnum(hdr.ar_size, 10, 10);
  177.       continue;
  178.     }
  179.  
  180.     /* read in extended filenames file */
  181.     if (filenum <= 2
  182.     && (strncmp(hdr.ar_name, "ARFILENAMES/    ", 16) == 0
  183.     ||  strncmp(hdr.ar_name, "//              ", 16) == 0)) {
  184.  
  185.       skiplen = 0; /* because we're reading it in, not skipping it */
  186.       extnameslen = namelen = ar_readnum(hdr.ar_size, 10, 10);
  187.       if (!namelen) { err = XADERR_DATAFORMAT; break; }
  188.  
  189.       ext_names = xadAllocVec(namelen, 0);
  190.       if (!ext_names) { err = XADERR_NOMEMORY; break; }
  191.       if ((err = xadHookAccess(XADAC_READ, namelen, (APTR) ext_names, ai))) {
  192.         break;
  193.       }
  194.  
  195.       /* turn newlines or slash-newlines to null bytes */
  196.       for (namep = ext_names; namelen--; namep++) {
  197.         if (*namep == 0x0A) namep[(namep[-1] == '/') ? -1 : 0] = '\0';
  198.       }
  199.       continue;
  200.     }
  201.     
  202.  
  203.     /* 'real' filenames processing */
  204.     namep = NULL;
  205.  
  206.     /* if there is an extended names file read in, and the name begins
  207.      * with a slash or space (except when it ends in a slash)
  208.      */
  209.     if (ext_names && (hdr.ar_name[0] == '/' || (hdr.ar_name[0] == ' '
  210.     && !ar_memchr(hdr.ar_name, '/', 16)))) {
  211.  
  212.       /* extended filename in extended filenames header */
  213.       namelen = ar_readnum(&hdr.ar_name[1], 15, 10); /* name offset */
  214.       if (namelen < extnameslen) {
  215.         namep = ext_names + namelen;
  216.         namelen = strlen(namep);
  217.       }
  218.     }
  219.     /* if the name begins "#1/x" where x is a number */
  220.     else if (hdr.ar_name[0] == '#'
  221.          &&  hdr.ar_name[1] == '1'
  222.          &&  hdr.ar_name[2] == '/'
  223.          && (hdr.ar_name[3] >= '0' && hdr.ar_name[3] <= '9') ) {
  224.  
  225.       /* extended filename after header */
  226.       if ((namelen = ar_readnum(&hdr.ar_name[3], 13, 10))) {
  227.         if (namelen < ar_readnum(hdr.ar_size, 10, 10)) {
  228.           /* allocate space to read in name */
  229.           bsd_name = xadAllocVec(namelen, 0);
  230.           if (!bsd_name) { err = XADERR_NOMEMORY; break; }
  231.  
  232.           /* read in extended name */
  233.           if ((err = xadHookAccess(XADAC_READ, namelen, (APTR) bsd_name, ai)))
  234.             break;
  235.  
  236.           namep = bsd_name;
  237.         }
  238.       }
  239.     }
  240.  
  241.     if (!namep) {
  242.       /* normal filename */
  243.       namep = hdr.ar_name;
  244.  
  245.       /* look for terminator - null, slash or space (in that order) */
  246.       if (!(pend = ar_memchr(namep, '\0', 16))
  247.       &&  !(pend = ar_memchr(namep, '/',  16)))
  248.             pend = ar_memchr(namep, ' ',  16);
  249.  
  250.       namelen = (pend) ? pend-namep : 16;
  251.     }
  252.  
  253.  
  254.     filetags[0].ti_Data = namelen + 1;
  255.     fi = (struct xadFileInfo *) xadAllocObjectA(XADOBJ_FILEINFO, filetags);
  256.     if (!fi) { err = XADERR_NOMEMORY; break; }
  257.  
  258.     fi->xfi_EntryNumber = filenum++;
  259.     fi->xfi_OwnerUID    = ar_readnum(hdr.ar_uid, 6, 10);
  260.     fi->xfi_OwnerGID    = ar_readnum(hdr.ar_gid, 6, 10);
  261.     fi->xfi_Size        = ar_readnum(hdr.ar_size, 10, 10);
  262.     fi->xfi_Flags       = XADFIF_SEEKDATAPOS;
  263.     fi->xfi_DataPos     = ai->xai_InPos;
  264.  
  265.     /* copy filename */
  266.     xadCopyMem(namep, fi->xfi_FileName, namelen);
  267.     fi->xfi_FileName[namelen] = '\0';
  268.  
  269.     /* bsd extended filename consumes part of the file size! */
  270.     if (bsd_name) {
  271.       xadFreeObjectA(bsd_name, NULL); bsd_name = NULL;
  272.       fi->xfi_Size -= namelen;
  273.     }
  274.     fi->xfi_CrunchSize  = skiplen = fi->xfi_Size;
  275.  
  276.     /* fix MS-DOS filenames */
  277.     for (namep = fi->xfi_FileName; namelen--; namep++) {
  278.       if (*namep == '\\') *namep = '/';
  279.     }
  280.  
  281.     prottags[0].ti_Data = ar_readnum(hdr.ar_mode, 8, 8);
  282.     prottags[1].ti_Data = (ULONG) &fi->xfi_Protection;
  283.     xadConvertProtectionA(prottags);
  284.  
  285.     datetags[0].ti_Data = ar_readnum(hdr.ar_date, 12, 10);
  286.     datetags[1].ti_Data = (ULONG) &fi->xfi_Date;
  287.     xadConvertDatesA(datetags);
  288.  
  289.     if (link) link->xfi_Next = fi; else ai->xai_FileInfo = fi;
  290.     link = fi;
  291.   }
  292.  
  293.   if (ext_names) xadFreeObjectA(ext_names, NULL);
  294.   if (bsd_name)  xadFreeObjectA(bsd_name,  NULL);
  295.  
  296.   if (err) {
  297.     if (!ai->xai_FileInfo) return err;
  298.     ai->xai_Flags |= XADAIF_FILECORRUPT;
  299.     ai->xai_LastError = err;
  300.   }
  301.   return XADERR_OK;
  302. }
  303.  
  304. ASM(LONG) ar_UnArchive(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
  305.   return xadHookAccess(XADAC_COPY, ai->xai_CurFile->xfi_Size, NULL, ai);
  306. }
  307.  
  308. const struct xadClient ar_Client = {
  309.   NEXTCLIENT, XADCLIENT_VERSION, 6, AR_VERSION, AR_REVISION,
  310.   8, XADCF_FILEARCHIVER | XADCF_FREEFILEINFO,
  311.   0, "Ar",
  312.  
  313.   /* client functions */
  314.   (BOOL (*)()) ar_RecogData,
  315.   (LONG (*)()) ar_GetInfo,
  316.   (LONG (*)()) ar_UnArchive,
  317.   NULL
  318. };
  319.